#include <QSqlQuery>
#include <QThread>
#include "Const.h"
#include "Server.h"

/**
 * 构造方法。
 */
Server::Server(): 
    mDatabase(QSqlDatabase::addDatabase(Const::DB_DRIVER)) {
    // 创建数据库
    this->createDatabase();

    // 连接信号和槽
    QTcpServer::connect(this, SIGNAL(newConnection()), SLOT(onAccept()));
}

/**
 * 析构方法。
 */
Server::~Server() {
    // 关闭服务
    QTcpServer::close();

    // 关闭数据库
    mDatabase.close();

    // 清空在线用户列表
    OnlineUsers::iterator end = mOnlineUsers.end();
    for (OnlineUsers::iterator i = mOnlineUsers.begin(); i != end; i++) {
        delete i.value();
    }
    mOnlineUsers.clear();
}

/**
 * 客户端连接服务器事件的响应方法。
 */
void Server::onAccept() {
    QTcpSocket* clientSocket = QTcpServer::nextPendingConnection();
    new Client(this, clientSocket);
}

/**
 * 创建数据库。
 */
void Server::createDatabase() {
    // 打开数据库
    mDatabase.setDatabaseName(Const::DB_PATH);
    mDatabase.open();

    // 创建表
    QSqlQuery query(mDatabase);
    QString sql;

    /* 用户表 */
    sql = QString("CREATE TABLE IF NOT EXISTS %0(%1 TEXT PRIMARY KEY,%2 TEXT,%3 INTEGER)").
        arg(Const::TABLE_USERS, Const::FIELD_NAME, Const::FIELD_PASSWORD, Const::FIELD_ICON_INDEX);
    query.exec(sql);

    /* 好友表 */
    sql = QString("CREATE TABLE IF NOT EXISTS %0(%1 TEXT,%2 TEXT)").
        arg(Const::TABLE_FRIENDS, Const::FIELD_OWNER, Const::FIELD_FRIEND_NAME);
    query.exec(sql);

    /* 离线聊天表 */
    sql = QString("CREATE TABLE IF NOT EXISTS %0(%1 TEXT,%2 TEXT,%3 TEXT,%4 TEXT)").
        arg(Const::TABLE_OFFLINE_CHAT, Const::FIELD_DST_USER, Const::FIELD_SRC_USER, Const::FIELD_CONTENT, Const::FIELD_TIME);
    query.exec(sql);

    /* 离线添加好友表 */
    sql = QString("CREATE TABLE IF NOT EXISTS %0(%1 TEXT,%2 TEXT,%3 INTEGER)").
        arg(Const::TABLE_OFFLINE_ADD_FRIEND, Const::FIELD_SRC_USER, Const::FIELD_DST_USER, Const::FIELD_RESULT);
    query.exec(sql);
}

/**
 * 处理添加好友请求。
 * @param request   添加好友请求
 * @param client    客户端
 */
void Server::handleAddFriendRequest(AddFriendRequest* request, Client* client) {
    QSqlQuery query(mDatabase);
    QString sql;

    // 检查目标用户是否存在
    sql = QString("SELECT * FROM %0 WHERE %1='%2'").arg(Const::TABLE_USERS, Const::FIELD_NAME, request->mDstUsername);
    query.exec(sql);
    if (!query.next()) {
        // 查无此人，通知源用户
        AddFriendResult result(0, request->mDstUsername, USER_NOT_FOUND, nullptr);
        this->sendData(client, &result, sizeof(result));
        query.clear();
        return;
    }

    // 检查是否已经是好友了
    sql = QString("SELECT * FROM %0 WHERE %1='%2' AND %3='%4'").arg(Const::TABLE_FRIENDS,
        Const::FIELD_OWNER, request->mSrcUsername, Const::FIELD_FRIEND_NAME, request->mDstUsername);
    query.exec(sql);
    if (query.next()) {
        // 已经是好友了，通知源用户
        AddFriendResult result(0, request->mDstUsername, ALREADY_BE_FRIENDS, nullptr);
        this->sendData(client, &result, sizeof(result));
        query.clear();
        return;
    }
    query.clear();

    // 如果目标用户不在线，则将请求信息插入到数据库中
    OnlineUsers::iterator it = mOnlineUsers.find(request->mDstUsername);
    if (it == mOnlineUsers.end()) {
        sql = QString("INSERT INTO %0 (%1,%2) VALUES('%3','%4')").arg(Const::TABLE_OFFLINE_ADD_FRIEND,
            Const::FIELD_SRC_USER, Const::FIELD_DST_USER, request->mSrcUsername, request->mDstUsername);
        query.exec(sql);
        query.clear();
        return;
    }

    // 向目标用户发送请求
    this->sendData(it.value(), request, sizeof(AddFriendRequest));
}

/**
 * 处理添加好友结果。
 * @param result    添加好友结果
 */
void Server::handleAddFriendResult(AddFriendResult* result) {
    QSqlQuery query(mDatabase);
    QString sql;

    // 如果结果是同意，则将相关信息写到好友表中
    if (result->mResultCode == ADD_FRIEND_ALLOW) {
        sql = QString("INSERT INTO %0 VALUES('%1','%2')").arg(Const::TABLE_FRIENDS, result->mSrcUsername, result->mDstUsername);
        query.exec(sql);

        sql = QString("INSERT INTO %0 VALUES('%1','%2')").arg(Const::TABLE_FRIENDS, result->mDstUsername, result->mSrcUsername);
        query.exec(sql);
    }
    query.clear();

    // 如果目标用户不在线，则将结果信息插入到数据库中
    OnlineUsers::iterator it = mOnlineUsers.find(result->mSrcUsername);
    if (it == mOnlineUsers.end()) {
        sql = QString("INSERT INTO %0 VALUES('%1','%2','%3')").
            arg(Const::TABLE_OFFLINE_ADD_FRIEND, result->mSrcUsername, result->mDstUsername, QString::number(result->mResultCode));
        query.exec(sql);
        query.clear();
        return;
    }

    // 向目标用户发送结果
    this->sendData(it.value(), result, sizeof(AddFriendResult));
}

/**
 * 处理聊天。
 * @param chatInfo  聊天信息
 */
void Server::handleChat(ChatInfo* chatInfo) {
    // 处理群发消息
    if (strcmp(ImProtocol::MULTI_CHAT, chatInfo->mDstUsername) == 0) {
        OnlineUsers::iterator end = mOnlineUsers.end();
        for (OnlineUsers::iterator i = mOnlineUsers.begin(); i != end; i++) {
            // 跳过自己，向其它用户发送聊天信息
            if (i.key() != chatInfo->mSrcUsername) {
                this->sendData(i.value(), chatInfo, sizeof(ChatInfo));
            }
        }
        return;
    }

    // 如果目标用户不在线，则将聊天信息插入到数据库中
    OnlineUsers::iterator it = mOnlineUsers.find(chatInfo->mDstUsername);
    if (it == mOnlineUsers.end()) {
        QSqlQuery query(mDatabase);
        QString sql = QString("INSERT INTO %0 VALUES('%1','%2','%3','%4')").
            arg(Const::TABLE_OFFLINE_CHAT, chatInfo->mDstUsername, chatInfo->mSrcUsername, chatInfo->mContent, chatInfo->mTime);
        query.exec(sql);
        query.clear();
        return;
    }

    // 发送给目标用户
    this->sendData(it.value(), chatInfo, sizeof(ChatInfo));
}

/**
 * 处理离线请求。
 * @param request   离线请求
 */
void Server::handleLogoff(LogoffRequest* request) {
    // 将该用户从在线列表移除
    const char* username = request->mUsername;
    mOnlineUsers.remove(username);

    // 广播该用户的离线请求
    OnlineUsers::iterator end = mOnlineUsers.end();
    for (OnlineUsers::iterator it = mOnlineUsers.begin(); it != end; it++) {
        this->sendData(it.value(), request, sizeof(LogoffRequest));
    }
}

/**
 * 处理登录请求。
 * @param request   登录请求
 * @param client    客户端
 */
void Server::handleLogon(LogonRequest* request, Client* client) {
    LogonResult logonResult;
    QSqlQuery query(mDatabase);
    QString sql;

    // 检查该用户是否已经登录
    OnlineUsers::iterator it = mOnlineUsers.find(request->mUserInfo.mUsername);
    if (it != mOnlineUsers.end()) {
        // 已经登录了
        logonResult.mResultCode = USER_ALREADY_LOGON;
        this->sendData(client, &logonResult, sizeof(logonResult));
        return;
    }

    // 从数据库中查找该用户
    sql = QString("SELECT * FROM %0 WHERE %1='%2'").arg(Const::TABLE_USERS, Const::FIELD_NAME, request->mUserInfo.mUsername);
    query.exec(sql);
    if (!query.next()) {
        // 查无此人
        query.clear();
        logonResult.mResultCode = USER_NOT_FOUND;
        this->sendData(client, &logonResult, sizeof(logonResult));
        return;
    }

    // 比对密码
    const QString password(query.value(Const::FIELD_PASSWORD).toString());
    if (password != request->mUserInfo.mPassword) {
        // 密码不正确
        query.clear();
        logonResult.mResultCode = PASSWORD_WRONG;
        this->sendData(client, &logonResult, sizeof(logonResult));
        return;
    }

    // 登录成功，获取该用户的相关信息
    logonResult.mResultCode = LOGON_SUCCEEDED;
    logonResult.mIconIndex = query.value(Const::FIELD_ICON_INDEX).toInt();
    query.clear();

    // 获取该用户的好友列表
    sql = QString("SELECT * FROM %0 WHERE %1='%2'").arg(Const::TABLE_FRIENDS, Const::FIELD_OWNER, request->mUserInfo.mUsername);
    query.exec(sql);

    int friendCount = 0;
    QString friendName;
    bool isOnline = false;

    for (friendCount = 0; query.next(); friendCount++) {
        friendName = query.value(Const::FIELD_FRIEND_NAME).toString();
        strcpy_s(logonResult.mFriends[friendCount].mUsername, ImProtocol::USERNAME_BUF_LEN, friendName.toStdString().c_str());

        sql = QString("SELECT %0 FROM %1 WHERE %2='%3'").arg(Const::FIELD_ICON_INDEX, Const::TABLE_USERS, Const::FIELD_NAME, friendName);
        QSqlQuery iconIndexQuery(mDatabase);
        iconIndexQuery.exec(sql);
        iconIndexQuery.next();
        logonResult.mFriends[friendCount].mIconIndex = iconIndexQuery.value(Const::FIELD_ICON_INDEX).toInt();
        iconIndexQuery.clear();

        OnlineUsers::iterator it = mOnlineUsers.find(friendName);
        isOnline = it != mOnlineUsers.end();
        logonResult.mFriendsOnline[friendCount] = isOnline;

        // 如果好友在线，则向好友发送通知
        if (isOnline) {
            FriendLogon friendLogon(request->mUserInfo.mUsername);
            this->sendData(it.value(), &friendLogon, sizeof(friendLogon));
        }
    }
    query.clear();
    logonResult.mFriendCount = friendCount;

    // 发送登录结果
    this->sendData(client, &logonResult, sizeof(logonResult));

    // 查询是否有和该用户相关的离线添加好友请求，有则发送
    sql = QString("SELECT * FROM %0 WHERE %1='%2' AND %3 IS NULL")
        .arg(Const::TABLE_OFFLINE_ADD_FRIEND, Const::FIELD_DST_USER, request->mUserInfo.mUsername, Const::FIELD_RESULT);
    query.exec(sql);
    while (query.next()) {
        QString srcUsername = query.value(Const::FIELD_SRC_USER).toString();
        sql = QString("SELECT %0 FROM %1 WHERE %2='%3'").arg(Const::FIELD_ICON_INDEX, Const::TABLE_USERS, Const::FIELD_NAME, srcUsername);
        QSqlQuery iconIndexQuery(mDatabase);
        iconIndexQuery.exec(sql);
        iconIndexQuery.next();
        AddFriendRequest addFriendRequest(request->mUserInfo.mUsername,
            iconIndexQuery.value(Const::FIELD_ICON_INDEX).toInt(), srcUsername.toStdString().c_str());
        iconIndexQuery.clear();
        this->sendData(client, &addFriendRequest, sizeof(addFriendRequest));
    }
    query.clear();

    // 删除数据库中的离线添加好友请求
    sql = QString("DELETE FROM %0 WHERE %1='%2'").arg(Const::TABLE_OFFLINE_ADD_FRIEND, Const::FIELD_DST_USER, request->mUserInfo.mUsername);
    query.exec(sql);

    // 查询是否有和该用户相关的离线添加好友结果，有则发送
    sql = QString("SELECT * FROM %0 WHERE %1='%2' AND %3 IS NOT NULL")
        .arg(Const::TABLE_OFFLINE_ADD_FRIEND, Const::FIELD_SRC_USER, request->mUserInfo.mUsername, Const::FIELD_RESULT);
    query.exec(sql);
    while (query.next()) {
        QString dstUsername(query.value(Const::FIELD_DST_USER).toString());
        ResultCode result = static_cast<ResultCode>(query.value(Const::FIELD_RESULT).toInt());
        AddFriendResult addFriendResult(-1, dstUsername.toStdString().c_str(), result, request->mUserInfo.mUsername);
        this->sendData(client, &addFriendResult, sizeof(addFriendResult));
    }
    query.clear();

    // 删除数据库中的离线添加好友结果
    sql = QString("DELETE FROM %0 WHERE %1='%2'").arg(Const::TABLE_OFFLINE_ADD_FRIEND, Const::FIELD_SRC_USER, request->mUserInfo.mUsername);
    query.exec(sql);

    // 查询是否有和该用户相关的离线聊天消息，有则发送
    sql = QString("SELECT * FROM %0 WHERE %1='%2'").arg(Const::TABLE_OFFLINE_CHAT, Const::FIELD_DST_USER, request->mUserInfo.mUsername);
    query.exec(sql);
    while (query.next()) {
        QString srcUsername(query.value(Const::FIELD_SRC_USER).toString());
        QString content(query.value(Const::FIELD_CONTENT).toString());
        QString time(query.value(Const::FIELD_TIME).toString());
        ChatInfo chatInfo(content.toStdString().c_str(), request->mUserInfo.mUsername, srcUsername.toStdString().c_str(), time.toStdString().c_str());
        this->sendData(client, &chatInfo, sizeof(chatInfo));
    }
    query.clear();

    // 删除数据库中的离线聊天信息
    sql = QString("DELETE FROM %0 WHERE %1='%2'").arg(Const::TABLE_OFFLINE_CHAT, Const::FIELD_DST_USER, request->mUserInfo.mUsername);
    query.exec(sql);

    // 将该用户加入在线列表
    mOnlineUsers.insert(request->mUserInfo.mUsername, client);
}

/**
 * 处理注册请求。
 * @param request   注册请求
 * @param client    客户端
 */
void Server::handleReg(RegRequest* request, Client* client) {
    RegResult regResult;
    QSqlQuery query(mDatabase);
    QString sql;

    // 将该用户信息插入到数据库中
    sql = QString("INSERT INTO %0 VALUES('%1','%2','%3')").
        arg(Const::TABLE_USERS, request->mUserInfo.mUsername, request->mUserInfo.mPassword, QString::number(request->mUserInfo.mIconIndex));
    bool result = query.exec(sql);
    if (!result) {
        // 该用户已存在
        query.clear();
        regResult.mResultCode = USER_EXISTS;
        this->sendData(client, &regResult, sizeof(regResult));
        return;
    }
    query.clear();

    // 发送结果
    regResult.mResultCode = REG_SUCCEEDED;
    this->sendData(client, &regResult, sizeof(regResult));

    // 将该用户加入到在线列表中
    mOnlineUsers.insert(request->mUserInfo.mUsername, client);
}

/**
 * 处理客户端发送来的数据。
 * @param pro       协议
 * @param client    客户端
 */
void Server::onGetMessageFromClient(ImProtocol* pro, Client* client) {
    switch (pro->mType) {
        case ImProtocol::ADD_FRIEND_REQUEST:
            this->handleAddFriendRequest(reinterpret_cast<AddFriendRequest*>(pro), client);
            break;
        case ImProtocol::ADD_FRIEND_RESULT:
            this->handleAddFriendResult(reinterpret_cast<AddFriendResult*>(pro));
            break;
        case ImProtocol::CHAT:
            this->handleChat(reinterpret_cast<ChatInfo*>(pro));
            break;
        case ImProtocol::LOGOFF:
            this->handleLogoff(reinterpret_cast<LogoffRequest*>(pro));
            break;
        case ImProtocol::LOGON:
            this->handleLogon(reinterpret_cast<LogonRequest*>(pro), client);
            break;
        case ImProtocol::REG:
            this->handleReg(reinterpret_cast<RegRequest*>(pro), client);
            break;
        default:
            break;
    }
}

/**
 * 发送数据。
 * @param client    客户端
 * @param pro       数据
 * @param dataSize  数据长度
 */
void Server::sendData(Client* client, ImProtocol* pro, qint64 dataSize) {
    // 发送数据长度
    client->write(&dataSize, sizeof(dataSize));

    // 发送数据
    client->write(pro, dataSize);
    client->flush();

    // 防止发送过快导致客户端无法响应
    QThread::msleep(100);
}

/**
 * 开启服务。
 * @return 成功则返回 true，否则返回 false
 */
bool Server::start() {
    // 侦听端口
    QHostAddress hostAddress(ImProtocol::SERVER_IP);

    // 侦听
    return QTcpServer::listen(hostAddress, ImProtocol::SERVER_PORT);
}
